﻿//BankingSystem.cpp
#include<iostream>//包含输入输出流库，用于实现基本的输入输出操作，如 std::cout 和 std::cin
#include<string>//包含字符串处理库，用于处理字符串类型的数据，如存储客户姓名、账户 ID 等
#include<limits>//包含数值极限库，用于获取输入流的最大容量，在忽略输入缓冲区时会用到
#include<iomanip>//包含输入输出操作符库，用于设置输出的格式，如设置小数精度
#include"Bank.hh"//包含自定义的 Bank.hh 头文件，该头文件中定义了银行系统所需的类和函数声明
#include"BankingSystem.hh"//包含自定义的 BankingSystem.hh 头文件
#ifdef _WIN32
#include<Windows.h>
#endif
//使用#undef取消宏定义
#undef min
#undef max

using namespace Banking;//使用 Banking 命名空间，该命名空间中定义了银行系统的相关类和函数


//清屏函数，根据不同的操作系统执行不同的清屏命令
void clearScreen() {
#ifdef _WIN32//如果是 Windows 操作系统
    system("cls");//执行 Windows 系统的清屏命令
#else//如果是其他操作系统（如 Linux、Mac OS）
    system("clear");//执行相应系统的清屏命令
#endif
}

//等待用户按下回车键继续的函数
void waitForEnter() {

    std::cout << "\nPress Enter to continue...";//提示用户按下回车键继续
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');//忽略输入缓冲区中的所有字符，直到遇到换行符
}

//显示主菜单的函数
void displayMainMenu() {
    clearScreen();//清屏
    std::cout << "===== Banking System =====\n"//输出银行系统的标题
        << "1. Register new customer\n"//选项 1：注册新客户
        << "2. Access customer accounts\n"//选项 2：访问客户账户
        << "3. Display all bank accounts\n"//选项 3：显示所有银行账户
        << "4. Transaction reports\n"//选项 4：交易报告
        << "5. Exit\n"//选项 5：退出系统
        << "Please choose: ";//提示用户选择操作
}

//注册新客户的函数
void registerNewCustomer(Bank& bank) {
    clearScreen();//清屏
    std::cout << "===== Register New Customer =====\n";//输出注册新客户的标题

    std::string name;//定义一个字符串变量，用于存储客户姓名
    std::cout << "Enter customer name: ";//提示用户输入客户姓名
    std::getline(std::cin, name);//从标准输入读取一行字符串，存储到 name 变量中

    auto customer = bank.registerCustomer(name);//调用银行对象的 registerCustomer 方法，注册新客户，并返回一个指向客户对象的智能指针

    std::cout << "\nCustomer " << name << " successfully registered!\n"//输出注册成功的信息
        << "Two bank accounts have been created:\n"//提示已为客户创建两个银行账户
        << "- Savings Account: " << customer->getSavingsAccount()->getAccountId() << "\n"//输出储蓄账户的 ID
        << "- Checking Account: " << customer->getCheckingAccount()->getAccountId() << "\n";//输出支票账户的 ID

    waitForEnter();//等待用户按下回车键继续
}

//显示客户菜单的函数
void displayCustomerMenu(const std::shared_ptr<Customer>& customer) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== Customer: " << customer->getName() << " =====\n"//输出客户姓名的标题
        << "This customer has the following accounts:\n\n";//提示客户拥有的账户信息

    auto savings = customer->getSavingsAccount();//获取客户的储蓄账户对象
    auto checking = customer->getCheckingAccount();//获取客户的支票账户对象

    std::cout << "1. Savings Account: " << savings->getAccountId() << "\n"//输出储蓄账户的 ID
        << "   Balance: ¥" << std::fixed << std::setprecision(2) << savings->getBalance() << "\n\n"//输出储蓄账户的余额，保留两位小数
        << "2. Checking Account: " << checking->getAccountId() << "\n"//输出支票账户的 ID
        << "   Balance: ¥" << std::fixed << std::setprecision(2) << checking->getBalance() << "\n\n"//输出支票账户的余额，保留两位小数
        << "Options:\n"//输出操作选项的标题
        << "1. Access Savings Account\n"//选项 1：访问储蓄账户
        << "2. Access Checking Account\n"//选项 2：访问支票账户
        << "3. View all transactions for this customer\n"//选项 3：查看该客户的所有交易记录
        << "4. Generate customer report\n"//选项 4：生成客户报告
        << "5. Return to main menu\n"//选项 5：返回主菜单
        << "Please choose: ";//提示用户选择操作
}

//显示账户菜单的函数
void displayAccountMenu(const std::shared_ptr<Account>& account) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== Bank Account: " << account->getAccountId() << " ("//输出账户 ID 的标题
        << (dynamic_cast<SavingsAccount*>(account.get()) ? "Savings" : "Checking") << " Account) =====\n"//判断账户类型，输出储蓄账户或支票账户
        << "Account holder: " << account->getOwnerName() << "\n"//输出账户持有者的姓名
        << "Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n\n";//输出账户余额，保留两位小数

    if (dynamic_cast<CheckingAccount*>(account.get())) {//如果是支票账户
        std::cout << "1. Deposit\n"//选项 1：存款
            << "2. Withdraw\n";//选项 2：取款
    }

    std::cout << "3. Transfer\n"//选项 3：转账
        << "4. View transaction history\n"//选项 4：查看交易历史记录
        << "5. Generate bank account report\n"//选项 5：生成银行账户报告
        << "6. Return to customer page\n"//选项 6：返回客户页面
        << "Please choose: ";//提示用户选择操作
}

//执行存款操作的函数
void performDeposit(const std::shared_ptr<Account>& account) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== Deposit to Account: " << account->getAccountId() << " =====\n"//输出存款操作的标题
        << "Current Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n\n";//输出当前账户余额，保留两位小数

    double amount;//定义一个双精度浮点数变量，用于存储存款金额
    std::cout << "Enter amount to deposit: ";//提示用户输入存款金额
    std::cin >> amount;//从标准输入读取存款金额
    std::cin.ignore();//忽略输入缓冲区中的换行符

    std::string note;//定义一个字符串变量，用于存储存款备注
    std::cout << "Add note (optional): ";//提示用户输入存款备注
    std::getline(std::cin, note);//从标准输入读取一行字符串，存储到 note 变量中

    if (account->deposit(amount, note)) {//调用账户对象的 deposit 方法进行存款操作，如果操作成功
        std::cout << "\nDeposit successful!\n"//输出存款成功的信息
            << "New balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";//输出新的账户余额，保留两位小数
    }
    else {//如果操作失败
        std::cout << "\nDeposit failed!\n";//输出存款失败的信息
    }

    waitForEnter();//等待用户按下回车键继续
}

//执行取款操作的函数
void performWithdrawal(const std::shared_ptr<Account>& account) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== Withdraw from Account: " << account->getAccountId() << " =====\n"//输出取款操作的标题
        << "Current Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n\n";//输出当前账户余额，保留两位小数

    double amount;//定义一个双精度浮点数变量，用于存储取款金额
    std::cout << "Enter amount to withdraw: ";//提示用户输入取款金额
    std::cin >> amount;//从标准输入读取取款金额
    std::cin.ignore();//忽略输入缓冲区中的换行符

    std::string note;//定义一个字符串变量，用于存储取款备注
    std::cout << "Add note (optional): ";//提示用户输入取款备注
    std::getline(std::cin, note);//从标准输入读取一行字符串，存储到 note 变量中

    if (account->withdraw(amount, note)) {//调用账户对象的 withdraw 方法进行取款操作，如果操作成功
        std::cout << "\nWithdrawal successful!\n"//输出取款成功的信息
            << "New balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";//输出新的账户余额，保留两位小数
    }
    else {//如果操作失败
        std::cout << "\nWithdrawal failed!\n";//输出取款失败的信息
    }

    waitForEnter();//等待用户按下回车键继续
}

//执行转账操作的函数
void performTransfer(const std::shared_ptr<Account>& account, const Bank& bank) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== Transfer from Account: " << account->getAccountId() << " =====\n"//输出转账操作的标题
        << "Current Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n\n";//输出当前账户余额，保留两位小数

    std::string targetId;//定义一个字符串变量，用于存储目标账户的 ID
    std::cout << "Enter destination account ID: ";//提示用户输入目标账户的 ID
    std::getline(std::cin, targetId);//从标准输入读取目标账户的 ID

    auto targetAccount = bank.findAccount(targetId);//调用银行对象的 findAccount 方法，查找目标账户
    if (!targetAccount) {//如果未找到目标账户
        std::cout << "\nInvalid account ID!\n";//输出无效账户 ID 的信息
        waitForEnter();//等待用户按下回车键继续
        return;//结束函数
    }

    double amount;//定义一个双精度浮点数变量，用于存储转账金额
    std::cout << "Enter amount to transfer: ";//提示用户输入转账金额
    std::cin >> amount;//从标准输入读取转账金额
    std::cin.ignore();//忽略输入缓冲区中的换行符

    std::string note;//定义一个字符串变量，用于存储转账备注
    std::cout << "Add note (optional): ";//提示用户输入转账备注
    std::getline(std::cin, note);//从标准输入读取一行字符串，存储到 note 变量中

    if (account->transfer(targetAccount, amount, note)) {//调用账户对象的 transfer 方法进行转账操作，如果操作成功
        std::cout << "\nTransfer successful!\n"//输出转账成功的信息
            << "New balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";//输出新的账户余额，保留两位小数
    }
    else {//如果操作失败
        std::cout << "\nTransfer failed!\n";//输出转账失败的信息
    }

    waitForEnter();//等待用户按下回车键继续
}

//显示所有银行账户信息的函数
void displayAllAccounts(const Bank& bank) {
    clearScreen();//清屏
    SetConsoleOutputCP(CP_UTF8);//Windows系统下设置控制台输出为UTF-8编码
    std::cout << "===== All Bank Accounts =====\n\n";//输出所有银行账户信息的标题

    std::cout << "SAVINGS ACCOUNTS:\n";//输出储蓄账户信息的标题
    int savingsCount = 1;//定义一个整数变量，用于记录储蓄账户的序号
    for (const auto& account : bank.getAllAccounts()) {//遍历银行的所有账户
        if (dynamic_cast<SavingsAccount*>(account.get())) {//如果是储蓄账户
            std::cout << savingsCount++ << ". ID: " << account->getAccountId() << "  |  "//输出储蓄账户的序号和 ID
                << "Owner: " << account->getOwnerName() << "  |  "//输出储蓄账户的持有者姓名
                << "Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";//输出储蓄账户的余额，保留两位小数
        }
    }

    std::cout << "\nCHECKING ACCOUNTS:\n";//输出支票账户信息的标题
    int checkingCount = 1;//定义一个整数变量，用于记录支票账户的序号
    for (const auto& account : bank.getAllAccounts()) {//遍历银行的所有账户
        if (dynamic_cast<CheckingAccount*>(account.get())) {//如果是支票账户
            std::cout << checkingCount++ << ". ID: " << account->getAccountId() << "  |  "//输出支票账户的序号和 ID
                << "Owner: " << account->getOwnerName() << "  |  "//输出支票账户的持有者姓名
                << "Balance: ¥" << std::fixed << std::setprecision(2) << account->getBalance() << "\n";//输出支票账户的余额，保留两位小数
        }
    }

    std::cout << "\nTotal Accounts: " << bank.getTotalAccounts() << "\n"//输出银行的总账户数
        << "Total Customers: " << bank.getTotalCustomers() << "\n"//输出银行的总客户数
        << "Total Balance: ¥" << std::fixed << std::setprecision(2) << bank.getTotalBalance() << "\n";//输出银行的总余额，保留两位小数

    waitForEnter();//等待用户按下回车键继续
}


//运行银行系统的主函数
void runBankingSystem() {
    Bank bank;//创建一个银行对象
    bool running = true;//定义一个布尔变量，用于控制程序的运行状态

    while (running) {//当程序处于运行状态时
        displayMainMenu();//显示主菜单

        int choice;//定义一个整数变量，用于存储用户的选择
        std::cin >> choice;//从标准输入读取用户的选择
        std::cin.ignore();//忽略输入缓冲区中的换行符

        switch (choice) {//根据用户的选择执行相应的操作
        case 1: {//如果用户选择注册新客户
            registerNewCustomer(bank);//调用 registerNewCustomer 函数注册新客户
            break;//跳出 switch 语句
        }
        case 2: {//如果用户选择访问客户账户
            clearScreen();//清屏
            std::cout << "===== Access Customer Accounts =====\n";//输出访问客户账户的标题

            std::string name;//定义一个字符串变量，用于存储客户姓名
            std::cout << "Enter customer name: ";//提示用户输入客户姓名
            std::getline(std::cin, name);//从标准输入读取客户姓名

            auto customer = bank.findCustomer(name);//调用银行对象的 findCustomer 方法，查找客户
            if (!customer) {//如果未找到客户
                std::cout << "\nCustomer not found!\n";//输出客户未找到的信息
                waitForEnter();//等待用户按下回车键继续
                break;//跳出 switch 语句
            }

            bool inCustomerMenu = true;//定义一个布尔变量，用于控制客户菜单的运行状态
            while (inCustomerMenu) {//当客户菜单处于运行状态时
                displayCustomerMenu(customer);//显示客户菜单

                int customerChoice;//定义一个整数变量，用于存储用户在客户菜单中的选择
                std::cin >> customerChoice;//从标准输入读取用户的选择
                std::cin.ignore();//忽略输入缓冲区中的换行符

                switch (customerChoice) {//根据用户在客户菜单中的选择执行相应的操作
                case 1:
                case 2: {//如果用户选择访问储蓄账户或支票账户
                    std::shared_ptr<Account> account;
                    if (customerChoice == 1) {
                        account = customer->getSavingsAccount();
                    }
                    else {
                        account = customer->getCheckingAccount();
                    }//根据用户的选择获取相应的账户对象

                    bool inAccountMenu = true;//定义一个布尔变量，用于控制账户菜单的运行状态
                    while (inAccountMenu) {//当账户菜单处于运行状态时
                        displayAccountMenu(account);//显示账户菜单

                        int accountChoice;//定义一个整数变量，用于存储用户在账户菜单中的选择
                        std::cin >> accountChoice;//从标准输入读取用户的选择
                        std::cin.ignore();//忽略输入缓冲区中的换行符

                        if (dynamic_cast<CheckingAccount*>(account.get())) {//如果是支票账户
                            switch (accountChoice) {//根据用户在账户菜单中的选择执行相应的操作
                            case 1:
                                performDeposit(account);//调用 performDeposit 函数进行存款操作
                                break;//跳出 switch 语句
                            case 2:
                                performWithdrawal(account);//调用 performWithdrawal 函数进行取款操作
                                break;//跳出 switch 语句
                            case 3:
                                performTransfer(account, bank);//调用 performTransfer 函数进行转账操作
                                break;//跳出 switch 语句
                            case 4:
                                //查看交易历史记录
                                clearScreen();//清屏
                                for (const auto& transaction : account->getTransactions()) {//遍历账户的所有交易记录
                                    std::cout << transaction.toString() << "\n";//输出交易记录的字符串表示
                                }
                                waitForEnter();//等待用户按下回车键继续
                                break;//跳出 switch 语句
                            case 5:
                                account->generateTransactionReport();//调用账户对象的 generateTransactionReport 方法生成账户报告
                                std::cout << "\nReport generated successfully!\n";//输出报告生成成功的信息
                                waitForEnter();//等待用户按下回车键继续
                                break;//跳出 switch 语句
                            case 6:
                                inAccountMenu = false;//退出账户菜单
                                break;//跳出 switch 语句
                            }
                        }
                        else {//如果是储蓄账户
                            switch (accountChoice) {//根据用户在账户菜单中的选择执行相应的操作
                            case 3:
                                performTransfer(account, bank);//调用 performTransfer 函数进行转账操作
                                break;//跳出 switch 语句
                            case 4:
                                //查看交易历史记录
                                clearScreen();//清屏
                                for (const auto& transaction : (account->getTransactions())) {//遍历账户的所有交易记录
                                    std::cout << transaction.toString() << "\n";//输出交易记录的字符串表示
                                }
                                waitForEnter();//等待用户按下回车键继续
                                break;//跳出 switch 语句
                            case 5:
                                account->generateTransactionReport();//调用账户对象的 generateTransactionReport 方法生成账户报告
                                std::cout << "\nReport generated successfully!\n";//输出报告生成成功的信息
                                waitForEnter();//等待用户按下回车键继续
                                break;//跳出 switch 语句
                            case 6:
                                inAccountMenu = false;//退出账户菜单
                                break;//跳出 switch 语句
                            }
                        }

                    }
                    break;//跳出 switch 语句
                }
                case 3: {
                    //查看该客户的所有交易记录
                    clearScreen();//清屏
                    for (const auto& transaction : customer->getSavingsAccount()->getTransactions()) {//遍历客户储蓄账户的所有交易记录
                        std::cout << transaction.toString() << "\n";//输出交易记录的字符串表示
                    }
                    for (const auto& transaction : customer->getCheckingAccount()->getTransactions()) {//遍历客户支票账户的所有交易记录
                        std::cout << transaction.toString() << "\n";//输出交易记录的字符串表示
                    }
                    waitForEnter();//等待用户按下回车键继续
                    break;//跳出 switch 语句
                }
                case 4: {
                    customer->generateTransactionReport();//调用客户对象的 generateTransactionReport 方法生成客户报告
                    std::cout << "\nReport generated successfully!\n";//输出报告生成成功的信息
                    waitForEnter();//等待用户按下回车键继续
                    break;//跳出 switch 语句
                }
                case 5: {
                    inCustomerMenu = false;//退出客户菜单
                    break;//跳出 switch 语句
                }
                }
            }
            break;//跳出 switch 语句
        }
        case 3:
            displayAllAccounts(bank);//调用 displayAllAccounts 函数显示所有银行账户信息
            break;//跳出 switch 语句
        case 4:
            bank.generateGlobalTransactionReport();//调用银行对象的 generateGlobalTransactionReport 方法生成全局交易报告
            std::cout << "\nReport generated successfully!\n";//输出报告生成成功的信息
            waitForEnter();//等待用户按下回车键继续
            break;//跳出 switch 语句
        case 5:
            running = false;//退出程序
            break;//跳出 switch 语句
        }
    }


}